package com.clock.shiro;


import cn.hutool.core.util.StrUtil;
import com.clock.domain.entity.User;
import com.clock.service.RoleService;
import com.clock.service.UserService;
import com.clock.shiro.common.ShiroConstants;
import com.clock.shiro.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * 认证
 */
@Slf4j
@Component
public class AccountRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Resource
    private RoleService roleService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("进入授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        User user = (User)principals.getPrimaryPrincipal();

        //得到角色id
        Integer roleId = userService.getRoleId(user.getUserId());
        if(roleId==null){
            return info;
        }
        //获得角色的名字
        String roleName = roleService.getRoleName(roleId);
        //将角色信息存入角色列表
        info.addRole(roleName);

        return info;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("进入认证");
        String jwt = (String) token.getPrincipal();
        if (jwt == null) {
            throw new AuthenticationException(ShiroConstants.TOKEN_NULL);
        }
        // 校验token有效性
        this.checkUserTokenIsEffect(jwt);
        User user = new User();
        user.setUserId(Long.valueOf(JwtUtil.getUserId(jwt)));
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,jwt,getName());
        return info;
    }

    /**
     * 校验token的有效性
     * @param token
     */
    public Boolean checkUserTokenIsEffect(String token) throws AuthenticationException {
        // 解密获得userId，用于和数据库进行对比
        String userId = JwtUtil.getUserId(token);
        if (userId == null) {
            throw new AuthenticationException(ShiroConstants.TOKEN_ILLEGAL);
        }

        // 校验token
        if (!jwtTokenRefresh(token, userId)) {
            throw new IncorrectCredentialsException(ShiroConstants.TOKEN_FAILURE);
        }
        return true;
    }

    /**
     * 对token进行一个判断
     * @return
     */
    public boolean jwtTokenRefresh(String token, String userId) {
        //如果缓存中的token为空，直接返回失效异常
        String cacheToken = stringRedisTemplate.opsForValue().get(ShiroConstants.PREFIX_USER_TOKEN + userId);
        if (!StrUtil.isBlank(cacheToken)&&cacheToken.equals(token)) {
            //刷新token在缓存中的淘汰时间
            stringRedisTemplate.expire(ShiroConstants.PREFIX_USER_TOKEN + userId,2, TimeUnit.HOURS);

            // 校验token有效性
            if (!JwtUtil.verify(token, userId)) {
                return false;
            }
            return true;
        }
        return false;
    }


    /**
     * 清除当前用户的权限认证缓存和用户token
     *
     * @param principals 权限信息
     */
    @Override
    public void clearCache(PrincipalCollection principals) {
        User user = (User)principals.getPrimaryPrincipal();
        //删除Token的缓存
        stringRedisTemplate.delete(ShiroConstants.PREFIX_USER_TOKEN+user.getUserId());
        super.clearCache(principals);
    }

    /**
     * 重写方法,清除当前用户的的 授权缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }
    /**
     * 重写方法，清除当前用户的 认证缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

}
